SPB Git

spb/autoscholar

Public
4.1 KB · 107 lines tsx
1import { listTopics } from "@autoscholar/core";2import { papers, paperTopics, paperVersions, series } from "@autoscholar/db";3import type { PublicPaperSummary } from "@autoscholar/schemas";4import { and, desc, eq, inArray } from "drizzle-orm";5import type { Metadata } from "next";6import Link from "next/link";7import { notFound } from "next/navigation";8import { Disclosure } from "@/components/Disclosure";9import { EmptyState } from "@/components/EmptyState";10import { PaperCard } from "@/components/PaperCard";11import { db } from "@/lib/server/db";1213export const dynamic = "force-dynamic";1415export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {16  const { slug } = await params;17  const t = (await listTopics(db())).find((x) => x.slug === slug);18  return { title: t ? `${t.name} — topic` : "Topic", alternates: { canonical: `/topics/${slug}` } };19}2021export default async function TopicPage({ params }: { params: Promise<{ slug: string }> }) {22  const { slug } = await params;23  const d = db();24  const topics = await listTopics(d);25  const topic = topics.find((x) => x.slug === slug);26  if (!topic) notFound();27  const children = topics.filter((c) => c.parentId === topic.id);28  const parent = topics.find((c) => c.id === topic.parentId);29  const ids = [topic.id, ...children.map((c) => c.id)];30  const staging = process.env.STAGING_MODE === "1";31  const rows = await d32    .selectDistinctOn([papers.id], { p: papers, v: paperVersions, s: { slug: series.slug } })33    .from(paperTopics)34    .innerJoin(papers, eq(paperTopics.paperId, papers.id))35    .innerJoin(paperVersions, eq(papers.currentVersionId, paperVersions.id))36    .leftJoin(series, eq(papers.seriesId, series.id))37    .where(and(inArray(paperTopics.topicId, ids), staging ? undefined : eq(papers.synthetic, false)))38    .orderBy(papers.id, desc(papers.firstPublishedAt));39  const items: PublicPaperSummary[] = rows40    .map((r) => ({41      publicId: r.p.publicId,42      slug: r.p.slug,43      title: r.v.title,44      abstract: r.v.abstract,45      keywords: r.v.keywords,46      discipline: r.p.discipline,47      researchMode: r.p.researchMode,48      status: r.p.status as PublicPaperSummary["status"],49      versionNo: r.v.versionNo,50      firstPublishedAt: r.p.firstPublishedAt.toISOString(),51      updatedAt: r.p.updatedAt.toISOString(),52      seriesSlug: r.s?.slug ?? null,53      topics: [],54      hasCode: r.v.artifactSet.some((a) => a.kind === "code"),55      hasData: r.v.artifactSet.some((a) => a.kind === "data" && !/README|manifest/.test(a.path)),56      hasLatex: r.v.artifactSet.some((a) => a.path.endsWith(".tex")),57      synthetic: r.p.synthetic,58    }))59    .sort((a, b) => b.firstPublishedAt.localeCompare(a.firstPublishedAt));60  return (61    <div className="container-x section-tight space-y-8">62      <header className="max-w-2xl">63        <p className="eyebrow">64          <Link href="/topics">Topics</Link>65          {parent && (66            <>67              {" "}68              / <Link href={`/topics/${parent.slug}`}>{parent.name}</Link>69            </>70          )}71        </p>72        <h1 className="title-1 mt-2">{topic.name}</h1>73        {topic.description && <p className="muted mt-2 max-w-2xl text-sm">{topic.description}</p>}74        {children.length > 0 && (75          <ul className="mt-3 flex flex-wrap gap-1.5" aria-label="Subfields">76            {children.map((c) => (77              <li key={c.id}>78                <Link79                  href={`/topics/${c.slug}`}80                  className="chip no-underline"81                  style={{ minHeight: "1.75rem" }}82                >83                  {c.name}84                </Link>85              </li>86            ))}87          </ul>88        )}89      </header>90      {items.length === 0 ? (91        <EmptyState title={`No working papers in ${topic.name} yet.`}>92          Papers appear here once they pass every quality gate.93        </EmptyState>94      ) : (95        <ul className="border-t hairline">96          {items.map((p, i) => (97            <li key={p.publicId}>98              <PaperCard p={p} variant="row" index={i} />99            </li>100          ))}101        </ul>102      )}103      <Disclosure compact />104    </div>105  );106}107
Files · main
Menu

Theme